#include <GameEngine/Graphics/Meshes/trgl_mesh_creator.h>

#include <GameEngine/Graphics/Meshes/trgl_mesh.h>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

//=================================================================================

//! Constructor from a triangulated mesh
TrglMeshCreator::TrglMeshCreator(TrglMesh& mesh) :
	mesh_(mesh), mode_(MeshCreator::None), point_(0), name_("Default") {}

//! Destructor
TrglMeshCreator::~TrglMeshCreator() {}

//! Prepare the mesh for its filling
/*!
 *  This function cleans all vertices, normals, texture coordinates, lines
 *  and polygons that were previously stored by the mesh
 */
bool TrglMeshCreator::prepare_mesh_creation() {
	// Clear the mesh
	mesh_.vertices_.clear();
	mesh_.normals_.clear();
	mesh_.coords_.clear();
	mesh_.trgl_groups_.clear();

	// Clean creator state
	set_group_name("Default");
	mode_ = MeshCreator::None;
	point_ = 0;
	subgroup_.clear();

	// Return success
	return true;
}

//! Terminates the mesh filling
bool TrglMeshCreator::finish_mesh_creation() {
	// Clean creator state
	mode_ = MeshCreator::None;
	point_ = 0;
	set_group_name("Default");

	// Return success
	return true;
}

//! Add a vertex to the mesh
bool TrglMeshCreator::add_vertex(const Point3d<float>& vertex) {
	mesh_.vertices_.push_back(vertex);
	return true;
}

//! Add a normal to the mesh
bool TrglMeshCreator::add_normal(const Vector3d<float>& normal) {
	mesh_.normals_.push_back(normal);
	return true;
}

//! Add a texture coordinate to the mesh
bool TrglMeshCreator::add_texture_coordinate(const TextureCoordinate2d& coord) {
	mesh_.coords_.push_back(coord);
	return true;
}

//! Set the group that receives the polygons and clear the stored polygons
/*!
 *  If the mesh creator is in a state where either a line or a polygon is
 *  being defined, this function is ignored
 */
bool TrglMeshCreator::set_group_name(const GEString& name) {
	// Check current mode
	if ( mode_ == MeshCreator::None ) {
		reset_subgroup();
	}

	// Change name
	name_ = name;
	return true;
}

//! Save the subgroup and clear the stored polygons
/*!
 *  If the mesh creator is in a state where either a line or a polygon is
 *  being defined, this function is ignored
 */
bool TrglMeshCreator::reset_subgroup() {
	if ( subgroup_.face_count() > 0 ) mesh_.add_subgroup(subgroup_,name_);
	subgroup_.clear();
	return true;
}

//! Starts the definition of a polygon
/*!
 *  If the mesh creator is already in a state where either a line or a
 *  polygon is being defined, this function is ignored
 */
bool TrglMeshCreator::begin_polygon() {
	// Check current mode
	if ( mode_ != MeshCreator::None ) return false;

	// Change the mode
	mode_ = MeshCreator::PolygonDef;
	point_ = 0;
	return true;
}

//! Terminates the definition of a polygon
/*!
 *  If the mesh creator is not already in a state where a polygon
 *  is being defined, this function is ignored
 */
bool TrglMeshCreator::end_polygon() {
	// Check current mode
	if ( mode_ != MeshCreator::PolygonDef ) return false;

	// Change mode
	mode_ = MeshCreator::None;
	return true;
}

//! Starts the definition of a line
/*!
 *  This implementation does nothing and always returns false
 */
bool TrglMeshCreator::begin_line() {
	return false;
}

//! Terminates the definition of a line
/*!
 *  This implementation does nothing and always returns false
 */
bool TrglMeshCreator::end_line() {
	return false;
}

//! Add a vertex index to the entity (line or polygon) currently being defined
/*!
 @param index The vertex index
 @param nindex The normal index
 @param cindex The texture coordinate index
 @return True if the vertex index could be added, false otherwise
 @note The index of the first vertex is 0 (as in a typical C++ notation), NOT 1
 */
bool TrglMeshCreator::add_vertex_index( SurfaceMesh::VertexIndex index,
										SurfaceMesh::NormalIndex nindex,
										SurfaceMesh::TextureCoordinateIndex cindex)
{
	// Check the current state
	if ( mode_ == MeshCreator::None ) return false;

	// Polygon definition
	if ( mode_ == MeshCreator::PolygonDef ) {
		// Insert the vertex in the triangle
		if ( point_ < 3 ) {
			trgl_.set_index(point_, index);
			trgl_.set_nindex(point_, nindex);
			trgl_.set_cindex(point_, cindex);
			++point_;
		}
		else {
			trgl_.set_index(1, trgl_.index3());
			trgl_.set_nindex(1, trgl_.nindex3());
			trgl_.set_cindex(1, trgl_.cindex3());
			trgl_.set_index(2, index);
			trgl_.set_nindex(2, nindex);
			trgl_.set_cindex(2, cindex);
		}

		// Add the triangle to the mesh if required
		if ( point_ >= 3 ) {
			bool has_normals = true;
			if ( trgl_.nindex1() < 0 ) has_normals = false;
			else if ( trgl_.nindex2() < 0 ) has_normals = false;
			else if ( trgl_.nindex3() < 0 ) has_normals = false;

			bool has_coords = true;
			if ( trgl_.cindex1() < 0 ) has_coords = false;
			else if ( trgl_.cindex2() < 0 ) has_coords = false;
			else if ( trgl_.cindex3() < 0 ) has_coords = false;

			trgl_.use_normals(has_normals);
			trgl_.use_texture_coordinates(has_coords);
			subgroup_.triangles().push_back(trgl_);
		}
		return true;
	}

	// Unknown state
	return false;
}

//! Accessor to the material of the triangle subgroup (read-write)
/*!
 @return A reference to the material of the triangle subgroup
 */
Material& TrglMeshCreator::subgroup_material() {
	return subgroup_.material();
}

//! Accessor to the material of the triangle subgroup (read-write)
/*!
 @return A reference to the use transparency flag of the triangle subgroup
 */
bool& TrglMeshCreator::subgroup_use_transparency() {
	// Texture is transparent
	return subgroup_.use_transparency();
}

//=================================================================================

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif
